#include <openssl/ec.h>
#include <openssl/objects.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <stdio.h>
#include <stdlib.h>

void handle_errors() {
    ERR_print_errors_fp(stderr);
    abort();
}

int main(int argc, char **argv) {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s <private_key.pem> <encrypted_file> <decrypted_file>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *private_key_file = argv[1];
    const char *encrypted_file = argv[2];
    const char *decrypted_file = argv[3];

    // Load private key
    EVP_PKEY *private_key = NULL;
    FILE *fp;
    fp = fopen(private_key_file, "r");
    if (!fp) {
        perror("Error opening private key file");
        return EXIT_FAILURE;
    }
    private_key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
    fclose(fp);
    if (!private_key) {
        handle_errors();
        return EXIT_FAILURE;
    }

    // Setup decryption
    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(private_key, NULL);
    if (!ctx) {
        handle_errors();
        return EXIT_FAILURE;
    }
    if (EVP_PKEY_decrypt_init(ctx) <= 0) {
        handle_errors();
        return EXIT_FAILURE;
    }

    // Read encrypted data
    FILE *fin = fopen(encrypted_file, "rb");
    if (!fin) {
        perror("Error opening encrypted file");
        return EXIT_FAILURE;
    }
    // Determine the size of the encrypted data
    fseek(fin, 0, SEEK_END);
    long encrypted_data_size = ftell(fin);
    fseek(fin, 0, SEEK_SET);
    unsigned char *encrypted_data = malloc(encrypted_data_size);
    fread(encrypted_data, 1, encrypted_data_size, fin);
    fclose(fin);

    // Decrypt data
    unsigned char *decrypted_data = NULL;
    size_t decrypted_data_size = 0;
    if (EVP_PKEY_decrypt(ctx, NULL, &decrypted_data_size, encrypted_data, encrypted_data_size) <= 0) {
        handle_errors();
        return EXIT_FAILURE;
    }
    decrypted_data = malloc(decrypted_data_size);
    if (EVP_PKEY_decrypt(ctx, decrypted_data, &decrypted_data_size, encrypted_data, encrypted_data_size) <= 0) {
        handle_errors();
        return EXIT_FAILURE;
    }

    // Write decrypted data to file
    FILE *fout = fopen(decrypted_file, "wb");
    if (!fout) {
        perror("Error opening decrypted file");
        return EXIT_FAILURE;
    }
    fwrite(decrypted_data, 1, decrypted_data_size, fout);
    fclose(fout);

    // Clean up
    EVP_PKEY_CTX_free(ctx);
    EVP_PKEY_free(private_key);
    free(encrypted_data);
    free(decrypted_data);

    printf("Decryption successful.\n");
    return EXIT_SUCCESS;
}
